// --------------------------------------------------------------------------------------------------------------------
// <copyright company="" file="SimpleLRUCache.cs">
//   
// </copyright>
// <fileheader>
// Name: SimpleLRUCache.cs
// Date: 10/26/2010
// Modified by Vincenzo Panza.
// </fileheader>
// <summary>
//   The simple lru cache.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

/* 
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
using System;
using System.Collections.Generic;

namespace Lucene.Net.Util.Cache
{
    /// <summary>
    /// The simple lru cache.
    /// </summary>
    public class SimpleLRUCache : SimpleMapCache
    {
        /////////////////////////////////////////////////////////////////////


        /////////////////////////////////////////////////////////////////////
        /// 
        /// <summary>
        /// The maximum number of items to cache.
        /// </summary>
        private int _capacity;

        /// <summary>
        /// The list to efficiently maintain the LRU state.
        /// </summary>
        private LinkedList<ListValueEntry> list;

        /// <summary>
        /// The dictionary to hash into any location in the list.
        /// </summary>
        private Dictionary<object, LinkedListNode<ListValueEntry>> lookup;

        /// <summary>
        /// The node instance to use/re-use when adding an item to the cache.
        /// </summary>
        private LinkedListNode<ListValueEntry> openNode;

        // private object[] cache;

        /// <summary>
        /// Initializes a new instance of the <see cref="SimpleLRUCache"/> class.
        /// </summary>
        /// <param name="capacity">
        /// The capacity.
        /// </param>
        public SimpleLRUCache(int capacity)
        {
            this._capacity = capacity;
            this.list = new LinkedList<ListValueEntry>();
            this.lookup = new Dictionary<object, LinkedListNode<ListValueEntry>>(capacity + 1);
            this.openNode = new LinkedListNode<ListValueEntry>(new ListValueEntry(null, null));

            // cache = new object[Capacity + 1];
        }

        /// <summary>
        /// The put.
        /// </summary>
        /// <param name="key">
        /// The key.
        /// </param>
        /// <param name="value">
        /// The value.
        /// </param>
        public override void Put(object key, object value)
        {
            {
                // if ( Get(Key) == null )
                // Console.WriteLine("put " + Value);
                this.openNode.Value.ItemKey = key;
                this.openNode.Value.ItemValue = value;
                this.list.AddFirst(this.openNode);
                this.lookup.Add(key, this.openNode);

                if (this.list.Count > this._capacity)
                {
                    // last node is to be removed and saved for the next addition to the cache
                    this.openNode = this.list.Last;

                    if (OnPurgedFromCache != null && OnPurgedFromCache.GetInvocationList().Length > 0)
                    {
                        OnPurgedFromCache(openNode.Value.ItemKey, openNode.Value.ItemValue);
                    }

                    // remove from list & dictionary
                    this.list.RemoveLast();
                    this.lookup.Remove(this.openNode.Value.ItemKey);
                }
                else
                {
                    // still filling the cache, create a new open node for the next time
                    this.openNode = new LinkedListNode<ListValueEntry>(new ListValueEntry(null, null));
                }
            }
        }

        /// <summary>
        /// The get.
        /// </summary>
        /// <param name="key">
        /// The key.
        /// </param>
        /// <returns>
        /// The get.
        /// </returns>
        public override object Get(object key)
        {
            LinkedListNode<ListValueEntry> node = null;
            if (!this.lookup.TryGetValue(key, out node))
            {
#if Debug
                Console.WriteLine("Cache MISS " + Key);
#endif
                return null;
            }

            this.list.Remove(node); // ??
            this.list.AddFirst(node); // ??
#if Debug
            Console.WriteLine("Cache HIT " + Key);
#endif
            return node.Value.ItemValue;
        }

        /// <summary>
        /// The remove.
        /// </summary>
        /// <param name="key">
        /// The key.
        /// </param>
        public void Remove(object key)
        {
            LinkedListNode<ListValueEntry> node = null;
            if (!this.lookup.TryGetValue(key, out node))
            {
                return;
            }

            this.list.Remove(node);
            this.lookup.Remove(node.Value.ItemKey);
        }

        /// <summary>
        /// The clear.
        /// </summary>
        public void Clear()
        {
            lookup.Clear();
            list.Clear();
        }

        /// <summary>
        /// The contains key.
        /// </summary>
        /// <param name="key">
        /// The key.
        /// </param>
        /// <returns>
        /// The contains key.
        /// </returns>
        public override bool ContainsKey(object key)
        {
            return Get(key) != null;
        }

        /// <summary>
        /// The this.
        /// </summary>
        /// <param name="key">
        /// The key.
        /// </param>
        public object this[object key]
        {
            get
            {
                /*
                //Console.WriteLine("get key: " + key + ";" + capacity);
                long i = ( (long)( key ) ) % capacity;
                //Console.WriteLine("get map to: " + i);
                try
                {
                    return cache[i];
                }
                catch
                {
                    return null;
                }
                 */
                if ( key == null ) return null;
                else
                    return Get(key);
            }

            set
            {
                /*
                long i = (long)( key ) % capacity;
                //Console.WriteLine("put map to: "+i);
                 cache[i] = value;
                 */
                if(key!=null) Put(key, value);
            }
        }

        /// <summary>
        /// The purged from cache delegate.
        /// </summary>
        /// <param name="key">
        /// The key.
        /// </param>
        /// <param name="value">
        /// The value.
        /// </param>
        public delegate void PurgedFromCacheDelegate(object key, object value);

        /// <summary>
        /// Event that is fired when an item falls outside of the cache
        /// </summary>
        public event PurgedFromCacheDelegate OnPurgedFromCache;

        /// <summary>
        /// Container to hold the key and value to aid in removal from 
        /// the <see cref="lookup"/> dictionary when an item is removed from cache.
        /// </summary>
        private class ListValueEntry
        {
            /// <summary>
            /// The item value.
            /// </summary>
            internal object ItemValue;

            /// <summary>
            /// The item key.
            /// </summary>
            internal object ItemKey;

            /// <summary>
            /// Initializes a new instance of the <see cref="ListValueEntry"/> class.
            /// </summary>
            /// <param name="key">
            /// The key.
            /// </param>
            /// <param name="value">
            /// The value.
            /// </param>
            internal ListValueEntry(object key, object value)
            {
                this.ItemKey = key;
                this.ItemValue = value;
            }
        }
    }

    #region NOT_USED_FROM_JLCA_PORT

    /*
  
 //
 // This is the oringal port as it was generated via JLCA.
 // This code is not used.  It's here for referance only.
 //
  

	/// <summary> Simple LRU cache implementation that uses a LinkedHashMap.
	/// This cache is not synchronized, use {@link Cache#SynchronizedCache(Cache)}
	/// if needed.
	/// 
	/// </summary>
	public class SimpleLRUCache:SimpleMapCache
	{
		private class AnonymousClassLinkedHashMap : LinkedHashMap
		{
			public AnonymousClassLinkedHashMap(SimpleLRUCache enclosingInstance)
			{
				InitBlock(enclosingInstance);
			}
			private void  InitBlock(SimpleLRUCache enclosingInstance)
			{
				this.enclosingInstance = enclosingInstance;
			}
			private SimpleLRUCache enclosingInstance;
			public SimpleLRUCache Enclosing_Instance
			{
				get
				{
					return enclosingInstance;
				}
				
			}
			protected internal virtual bool RemoveEldestEntry(System.Collections.DictionaryEntry eldest)
			{
				return size() > Enclosing_Instance.cacheSize;
			}
		}
		private const float LOADFACTOR = 0.75f;
		
		private int cacheSize;
		
		/// <summary> Creates a last-recently-used cache with the specified size. </summary>
		public SimpleLRUCache(int cacheSize):base(null)
		{
			this.cacheSize = cacheSize;
			int capacity = (int) System.Math.Ceiling(cacheSize / LOADFACTOR) + 1;
			
			base.map = new AnonymousClassLinkedHashMap(this, capacity, LOADFACTOR, true);
		}
	}
*/
    #endregion
}